import { ValidationArguments } from 'class-validator'
import { isPlainObject } from 'lodash'
import { I18nService, I18nValidationError, Path, TranslateOptions } from 'nestjs-i18n'

// i18n能识别的message标识符
export const I18N_VALIDATION_FLAG = '$$'
// 默认i18n的key前缀
export const I18N_VALIDATION_PREFIX = 'validation'

/**
 * 格式化校验错误，返回国际化后的消息
 *
 * @param errors
 * @param i18n
 * @param options
 * @returns
 */
export function formatI18nValidationErrors<K = Record<string, unknown>>(
  errors: I18nValidationError[],
  i18n: I18nService<K>,
  options?: TranslateOptions
): I18nValidationError[] {
  return errors.map(error => {
    error.children = formatI18nValidationErrors(error.children ?? [], i18n, options)
    error.constraints = Object.keys(error.constraints ?? {}).reduce((result, key) => {
      let translationKey = `${I18N_VALIDATION_PREFIX}.${strToHyphen(key)}`
      let args: any

      // 处理通过 {@link i18nMessage} 方法生成的message
      if (error.constraints[key]?.includes(I18N_VALIDATION_FLAG)) {
        const [customKey, argsString] = error.constraints[key].split(I18N_VALIDATION_FLAG)
        const customArgs = !!argsString ? JSON.parse(argsString) : {}

        if (customKey) {
          translationKey = customKey
        }
        args = customArgs
      }

      result[key] = i18n.translate(translationKey as Path<K>, {
        ...options,
        args: {
          property: error.property,
          value: error.value,
          target: error.target,
          contexts: error.contexts,
          ...args,
        },
      })
      return result
    }, {})
    return error
  })
}

/**
 * 生成i18n能识别的校验message
 *
 * @param key
 * @param args
 */
export function i18nMessage(
  key: string,
  args?: Record<keyof any, any> | Array<any>
): (validationArguments: ValidationArguments) => string
export function i18nMessage(
  args?: Record<keyof any, any> | Array<any>
): (validationArguments: ValidationArguments) => string
export function i18nMessage(): (validationArguments: ValidationArguments) => string
export function i18nMessage(
  arg0?: string | Record<keyof any, any> | Array<any>,
  arg1?: Record<keyof any, any> | Array<any>
) {
  let key = ''
  let args = undefined
  if (typeof arg0 === 'string') {
    key = arg0
    args = arg1
  } else if (isPlainObject(arg0) || Array.isArray(arg0)) {
    args = arg0
  }

  return (validationArguments: ValidationArguments) => {
    const { constraints, value } = validationArguments
    return [
      key,
      JSON.stringify({
        constraints,
        value: typeof value === 'string' ? value.replaceAll(I18N_VALIDATION_FLAG, '') : value,
        ...args,
      }),
    ].join(I18N_VALIDATION_FLAG)
  }
}

/**
 * 字符串转下划线
 * @param str 字符串
 */
function strToUnderline(str: string): string {
  return str ? str.replace(/([A-Z])/g, '_$1').toLowerCase() : String(str)
}

/**
 * 字符串转中划线
 * @param str
 * @returns
 */
function strToHyphen(str: string): string {
  return str ? strToUnderline(str).replace(/_/g, '-').replace(/^-*/, '') : String(str)
}
